using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using jQueryApi;

namespace Nsb.Classes
{
    public delegate void KeyAction(jQueryEvent e);

    [IgnoreNamespace]
    public static class Keys
    {
        private static readonly int[] ArrowCodes = { 37, 40 };
        private static readonly int[] VisibleCodes = { 48, 90, 96, 111, 186, 222 };
        private static readonly int[] BlackCodes = { 8, 8, 46, 90, 96, 111, 186, 222 };

        public static string MapKey(jQueryEvent e)
        {
            //string k = String.FromCharCode(e.Which);
            //return (((RegularExpression)Script.Literal(@"/[\w]/")).Test(k) ? k : e.Which.ToString()) + (e.ShiftKey ? "s" : "") + (e.CtrlKey ? "c" : "") + (e.AltKey ? "a" : "");
            return (IsKey(e.Which, VisibleCodes) ? String.FromCharCode(e.Which) : e.Which.ToString() + ":") + (e.CtrlKey ? "c" : "") + (e.ShiftKey ? "s" : "") + (e.AltKey ? "a" : "");
        }

        public static string DoKeys(jQueryEvent e, Dictionary<string, KeyAction> map)
        {
            string k = MapKey(e);
            KeyAction ka = map[k];
            if (ka != null)
            {
                ka(e);
                return null;
            }
            Inform.Debug("Unknown MapKey={0}", k);
            return k;
        }

        public static void FilterAll(jQueryObject el)
        {
            el.Bind("keydown.filterkeysdown", delegate(jQueryEvent e) { e.StopPropagation(); }); // Filter out keys to body
            el.Bind("keyup.filterkeysup", delegate(jQueryEvent e) { e.StopPropagation(); }); // Filter out keys to body
        }

        public static void RetKey(jQueryEvent e, jQueryEventHandler fn)
        {
            OnKey(e, 13, true, fn);
        }

        public static void ShiftRetKey(jQueryEvent e, jQueryEventHandler fn)
        {
            OnKey(e, 13, e.ShiftKey, fn);
        }

        public static void EscKey(jQueryEvent e, jQueryEventHandler fn)
        {
            OnKey(e, 27, true, fn);
        }

        public static void TabKey(jQueryEvent e, jQueryEventHandler fn)
        {
            OnKey(e, 9, true, fn);
        }

        public static void DocKeyDown(string ns, int code, bool quals, jQueryEventHandler handler)
        {
            jQuery.Document.Bind("keydown" + ns, delegate(jQueryEvent e) { OnKey(e, code, quals, handler); });
        }

        public static void DocKeyReleaseAll(string ns)
        {
            jQuery.Document.Unbind(ns);
        }

        /// <summary>
        /// Call a handler for the given key
        /// </summary>
        /// <param name="e">Key event</param>
        /// <param name="code">Key code</param>
        /// <param name="quals">key code qualifiers or'd together or just true for all qualifiers</param>
        /// <param name="handler">handler function</param>
        public static void OnKey(jQueryEvent e, int code, bool quals, jQueryEventHandler handler)
        {
            if (e.Which == code && quals)
            {
                DomElement.CancelEvent(e);
                handler(e);
            }
        }

        private static bool IsKey(int k, int[] codes)
        {
            for (int i = 0; i < codes.Length; i += 2)
            {
                if (k >= codes[i] && k <= codes[i + 1])
                    return true;
            }
            return false;
        }

        public static bool ArrowKey(jQueryEvent e)
        {
            return IsKey(e.Which, ArrowCodes);
        }

        public static bool BlackKey(jQueryEvent e)
        {
            return IsKey(e.Which, BlackCodes);
        }

        public static bool ChangeKey(jQueryEvent e)
        {
            //Inform.Log("Key {0} event {1} on element {2}", e.Which, e.Type, e.Target.TagName);
            switch (e.Target.TagName.ToLowerCase())
            {
                case "select":
                    return ArrowKey(e);
            }
            return BlackKey(e);
        }
    }
}
